﻿using PMS.Core.Configuration.CommonEnumConfigs;
using PMS.Core.Infrastructure;
using PMS.Core.Utils;
using PMS.Data.Entities.ScheduleTasks;
using PMS.Services.EntityServices.Logging;
using PMS.Services.EntityServices.SchduleTasks;
using Quartz;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace PMS.Services.Utils.ScheduleTasks
{
    public class TaskJobResult
    {
        public bool Status { get; set; }
        public string Message { get; set; }
    }
    
    /// <summary>
    /// Represents task manager
    /// </summary>
    public partial class TaskManager
    {
        #region Fields
        private IJobFactory _jobFactory;
        private ILogger _logger;
        private IScheduleTaskService _taskService;
        private ISchedulerFactory _schedulerFactory;
        private readonly List<SystemScheduleTask> _taskList = new List<SystemScheduleTask>();

        #endregion

        #region Ctor

        private TaskManager()
        {
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initializes the task manager
        /// DavidLee : 为使TaskManager全局单例化，并且保持构造函数能够实现Autofac依赖注入
        /// 对象内部数据初始化在此处理
        /// </summary>
        public void Initialize()
        {
            _taskList.Clear();
            if (_schedulerFactory == null)
                _schedulerFactory = EngineContext.Current.Resolve<ISchedulerFactory>();
            if(_jobFactory == null)
                _jobFactory = EngineContext.Current.Resolve<IJobFactory>();
            if(_logger == null)
                _logger = EngineContext.Current.Resolve<ILogger>();
            if(_taskService == null)
                _taskService = EngineContext.Current.Resolve<IScheduleTaskService>();

            var scheduleTasks = _taskService
                .GetAllTasks() // Filte Non Error Status and Stop Status Tasks
                .OrderBy(x => x.TaskName)
                .ToList();
            //TaskJobResult addjobResult = null;
            foreach (var scheduleTask in scheduleTasks)
            {
                //除Stop和Error状态外，其他状态任务在程序启动时可唤醒执行
                scheduleTask.Status = ScheduleJobActionStatus.Normal;
                //_taskService.UpdateTask(scheduleTask);
                AddScheduleJob(scheduleTask).GetAwaiter().GetResult();
            }
        }

        /// <summary>
        /// 启动作业,当无参数时为全部启动
        /// 当参数存在时，尝试启动指定任务
        /// </summary>
        public async Task<TaskJobResult> Start(SystemScheduleTask taskData = null)
        {
            TaskJobResult result = new TaskJobResult();
            if (taskData == null)
            {
                if (_taskList.Count > 0)
                {
                    int success = 0;
                    foreach (var task in _taskList)
                    {
                        result = _schedulerFactory.Start(task).GetAwaiter().GetResult();
                        if (result.Status)
                        {
                            success++;
                            task.Status = ScheduleJobActionStatus.Normal;
                            task.LastStartTimeUtc = DateTime.UtcNow;
                            _taskService.UpdateTask(task);
                        }
                    }
                    result.Status = true;
                    result.Message = $"任务启动完毕：成功{success}个,失败{_taskList.Count - success}个";
                }
                else
                {
                    result.Status = false;
                    result.Message = "没有可执行的任务";
                }
                return result;
            }
            else
            {
                result = await _schedulerFactory.Start(taskData);
                if (result.Status)
                {
                    taskData.Status = ScheduleJobActionStatus.Normal;
                    taskData.LastStartTimeUtc = DateTime.UtcNow;
                    _taskService.UpdateTask(taskData);
                }
                return result;
            }
        }

        /// <summary>
        /// Stops the task manager 同Start
        /// </summary>
        public async Task<TaskJobResult> Stop(SystemScheduleTask taskData = null)
        {
            TaskJobResult result = new TaskJobResult()
            {
                Status = false,
                Message = "没有可执行的任务"
            };
            if (taskData == null)
            {
                if (_taskList.Count > 0)
                {
                    IList<int> removeIndex = new List<int>();
                    int originTotal = _taskList.Count;
                    foreach (var (task, index) in _taskList.Select((value, i) => (value, i)))
                    {
                        result = _schedulerFactory.Stop(task).GetAwaiter().GetResult();
                        if (result.Status)
                        {
                            removeIndex.Add(index);
                            task.Status = ScheduleJobActionStatus.Stop;
                            task.LastStopTimeUtc = DateTime.UtcNow;
                            _taskService.UpdateTask(task);
                        }
                    }
                    if(removeIndex.Count > 0) 
                        foreach(int i in removeIndex)
                        {
                            _taskList.RemoveAt(i);
                        }
                    result.Status = true;
                    result.Message = $"任务停止完毕：成功{removeIndex.Count}个,失败{originTotal - removeIndex.Count}个";
                }
                
                return result;
            }
            else
            {
                result = await _schedulerFactory.Stop(taskData);
                if (result.Status)
                {
                    taskData.Status = ScheduleJobActionStatus.Stop;
                    taskData.LastStopTimeUtc = DateTime.UtcNow;
                    _taskService.UpdateTask(taskData);
                    if (IsJobExists(taskData.TaskName, taskData.GroupName))
                    {
                        //By default, object equality is compared by reference in .NET (unless Equals is overriden, every object inherits from object.Equals).
                        //If you want the Remove method to find your object, you cannot pass a new object.
                        //_taskList.Remove(taskData);
                        _taskList.Remove(_taskList.Where(t => t.Id == taskData.Id).FirstOrDefault());
                    }
                }
                return result;
            }
        }
        /// <summary>
        /// 暂停执行任务
        /// </summary>
        /// <param name="taskData"></param>
        /// <returns></returns>
        public Task<TaskJobResult> PauseJob(SystemScheduleTask taskData)
        {
            var result = _schedulerFactory.Pause(taskData);
            if(taskData.Status != ScheduleJobActionStatus.Paused)
            {
                if(taskData.Status != ScheduleJobActionStatus.Error
                 &&taskData.Status != ScheduleJobActionStatus.Stop)
                {
                    taskData.Status = ScheduleJobActionStatus.Paused;
                    taskData.LastStopTimeUtc = DateTime.UtcNow;
                    _taskService.UpdateTask(taskData);
                }
            }
            return result;
        }

        public Task<TaskJobResult> ExecuteJob(SystemScheduleTask taskData)
        {
            return _schedulerFactory.Run(taskData);
        }
        /// <summary>
        /// 执行添加并启动任务
        /// </summary>
        /// <param name="taskData"></param>
        /// <returns></returns>
        public async Task<TaskJobResult> AddScheduleJob(SystemScheduleTask taskData)
        {
            TaskJobResult result = new TaskJobResult();
            try
            {
                if (taskData == null)
                    throw new PMSException("Parameter of SystemScheduleTask is Null", SystemLogLevel.Error);
                (bool IsValid, string ValidateMsg) validExpression = taskData.CronExpression.IsValidExpression();
                if (!validExpression.IsValid)
                    throw new TaskCanceledException(validExpression.ValidateMsg);
                if (IsJobExists(taskData.TaskName, taskData.GroupName))
                    throw new TaskCanceledException($"The Schedule Task with the same TaskName:{taskData.TaskName} is already Exists.");
                
                IJobDetail job = JobBuilder.Create<TaskThread>()
                    .WithIdentity(taskData.TaskName, taskData.GroupName)
                    .Build();
                ITrigger trigger = TriggerBuilder.Create()
                   .WithIdentity(taskData.TaskName, taskData.GroupName)
                   .StartNow()
                   .WithDescription(taskData.Comment)
                   .WithCronSchedule(taskData.CronExpression)
                   .ForJob(taskData.TaskName, taskData.GroupName)
                   .Build();

                IScheduler scheduler = await _schedulerFactory.GetScheduler();
                                
                if (_jobFactory != null)
                    scheduler.JobFactory = _jobFactory;

                await scheduler.ScheduleJob(job, trigger);
                if (taskData.Status == (int)TriggerState.Normal)
                {//新添加任务初使化状态只能为Normal,否则不能自动启动运行
                    await scheduler.Start();
                    _taskService.UpdateTask(taskData);
                }
                else
                {
                    //框架启动时会自动添加执行任务
                    //任务如不能自动唤醒执行(Status == Normal),
                    //则可根据后台操作进行手动唤醒执行(因错误停止运行除外)
                    await PauseJob(taskData);
                    string errorMsg = $"新建任务(AddJob)失败暂停运行, 状态为:{Enum.GetName(typeof(ScheduleJobActionStatus), taskData.Status)}";
                    throw new PMSException(errorMsg);
                }
                //框架启动时会自动添加执行任务
                //区分是否需要数据入库记录
                if (taskData.Id <= 0) _taskService.InsertTask(taskData);
                result.Status = true;
                result.Message = "任务添加成功并启动运行";
                _taskList.Add(taskData);
                return result;
            }
            catch (Exception ex)
            {
                // Log
                result.Status = false;
                if (ex is PMSException && taskData == null)
                    result.Message = ex.Message;
                else if (ex is PMSException)
                    result.Message = string.Format(SystemScheduleTaskMessage.ScheduleTasksCanceledError, taskData.TaskName, taskData.GroupName, ex.Message);
                else
                    result.Message = ex.InnerException?.Message ?? ex.Message;
                _logger.Error(result.Message);
                return result;
            }
        }

        #endregion

        #region Utility
        public bool IsJobExists(string jobName, string groupName)
        {
            return _taskList.Any(t => t.TaskName == jobName && t.GroupName == groupName);
        }
        #endregion

        #region Properties

        /// <summary>
        /// Gets the task manger instance
        /// DavidLee 2020/06/29 改造TaskManager实例获取为全局单例模式
        /// </summary>
        public static TaskManager Instance //{ get; } = new TaskManager();
        {
            get
            {
                if (Singleton<TaskManager>.Instance == null)
                {
                    Create();
                }

                return Singleton<TaskManager>.Instance;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static TaskManager Create()
        {
            //create NowaEngine as engine
            return Singleton<TaskManager>.Instance ?? (Singleton<TaskManager>.Instance = new TaskManager());
        }

        /// <summary>
        /// Gets a list of task of this task manager
        /// </summary>
        public IList<SystemScheduleTask> Tasks => new ReadOnlyCollection<SystemScheduleTask>(_taskList);//new ReadOnlyCollection<SystemScheduleTask>(_taskList);

        #endregion
    }
}
